Alrighty, in this lecture, we're going to continue learning more about meta tables by using them to
imitate a programming paradigm called object oriented programming.
This lecture is just going to expose you to this new concept, as we take a much deeper dive into object
oriented programming later in the course.
So your first question is going to be what is object oriented programming?
Well, object oriented programming is a programming paradigm that allows us to create objects.
These objects hold their own unique properties and methods.
And in fact, the same paradigm is what the Roblox game engine uses for all of the instances or objects
in the game.
For example, the base plate in the game is an object created by something called the part class, and
we can see its type up here.
As you can see, it says part.
So this base plate is an object that belongs to the part class.
And the part class gives our object a whole bunch of different properties.
As you can see, all of these different properties belong to our part.
And the properties window and our part also has some other events and functions or methods that belong
to this particular object.
Now a class can be thought of as a blueprint, and this blueprint tells us how we can create as many
objects as we'd like that follow the standard of the blueprint.
But there only exists one blueprint.
For example, you can think of a blueprint for creating a car.
That blueprint tells you exactly how you can create the car, but you can modify the car.
Maybe you want to change the color, the engine.
Maybe you want to add tint to the windows.
There's ways for you to modify the object, but that one blueprint tells you the standard for creating
a car.
Now, Lua doesn't officially have a way to create classes, since Lua is considered to be a functional
programming language, but we can use tables and meta tables in Lua to mimic or imitate classes.
We can have a table act as our blueprint for creating and returning another table, and that table will
act as the object.
The table created by our class will have its own unique properties and methods, so it fulfills the
role of being an object.
Typically, these classes will have a function inside of them called a constructor.
The purpose of this function, as the name suggests, is to construct new objects that inherit properties
and functions from the class.
In fact, virtually all of the instances and data types in the Roblox API are created through constructors,
and these constructors are typically given the name of a dot new.
And that's what we're also going to be naming our constructor in this lecture.
So let's get started creating a new class.
And the way I'm going to show you how to create a class is the standard way of doing it in Lua.
Let's start off with a very simple example, and let's create a class for rectangles.
So let's create a new table or a new variable that's going to store a table.
And I'm going to call it rectangle.
Now you're going to notice that I'm naming it in Pascal case.
And typically you want to name any classes in Pascal case instead of naming it using the CamelCase naming
convention.
This way you can just differentiate your variables if it's going to be a class or if it's just, you
know, a regular variable with this naming convention.
But we're going to name it with the first letter being capitalized.
Now let's go ahead and create a constructor function for our class.
So this function is going to belong within this table.
And the way we can do that is we can first type out the function keyword.
We'll type out the name of the variable or the table which is going to be a rectangle.
And then we'll use a dot to say, hey, we're going to be creating a new function in this table, and
we're going to call it dot new.
And then we can go ahead and add our parentheses and then hit enter.
And here is our brand new constructor function that belongs inside of our rectangle table, which is
going to be acting as our class.
Now if we want to create a new rectangle we got to think what properties do rectangles have?
Well, a rectangle can have a width and it can have a height.
So let's define two parameters.
One is going to be our width and one is going to be our height.
Now our constructor has the responsibility of creating new objects.
So let's create an object.
Here we'll create a variable call it object.
And that's going to be equal to a brand new table.
So this table right here is going to act as the object inside of this table.
We can go ahead and define some properties for it.
For example the width which will set equal to the width passed to our constructor as well as the height.
We're going to set that equal to the height passed to our constructor.
Then we are able to return this object out of our constructor.
And boom!
Now we have a very basic working constructor.
So let's actually try it out here real quick.
Let's create.
Two separate rectangle objects.
And that's because these are going to be two different tables.
So let's create a variable called rect one.
So this is going to be our first rectangle.
We could do rectangle dot new.
And let's say the width and height of this one is going to be ten.
And then let's do rect two.
And that's going to also be equal to rectangle dot new.
And actually let's also do the same height and width of ten.
And to prove to you that these two tables are unique or basically these are two unique objects, let's
go ahead and print to see if rectangle one is equal to rectangle two.
So if we run this code, as you can see it's going to print false into the output.
And that's because these are two different tables in memory even though they are storing the same values.
So this allows us to maintain the uniqueness of our objects because these two objects aren't the same.
All right.
So our rectangle class is pretty basic right now.
And the objects created by our class are really only storing data which is width and height.
And we can't do much with this data.
But now let's go ahead and add some functions to our class that are going to be methods for our objects.
Now, if you want to create methods for your objects, you might think, okay, maybe we can define
some functions for our object inside of the constructor, right?
Maybe we want to have a function for our object.
So actually let's do that real quick.
Maybe we want to have a function for our object to get the area of the rectangle.
And we could define that here.
But there's actually a better way to define methods for our objects.
And we don't have to do it every single time a new object is created.
And the way that I'm going to show you how to do it also saves on memory, because every single object
will refer to these functions, and they won't have to create a new function and store it in each object
every single time we create a new rectangle.
So instead what we could do is we could create these methods within our rectangle class.
For example, we could create a get area function, and then we could have our objects be able to refer
to these functions by utilizing the underscore underscore index meta method, which we talked about
in the previous lecture.
Right now, even though this function exists within our rectangle class, if we were to create a new
rectangle object, we'll call this rect equal to rectangle dot new.
We'll just do a width and height of one.
If we try to refer to the function inside of our rectangle, as you can see, it only sees the width
and the height which we created in the object.
But it doesn't see this get area function.
And that's because this rectangle object doesn't have a meta table attached to it with the underscore
underscore index meta method.
So what we can do is we could actually use our rectangle class as a meta table.
So what we can do is we can create an underscore underscore index meta method inside of the rectangle
table.
And we want to set it equal to itself.
So that way we can attach this meta table to our object.
Let's do that right here.
Instead of returning the object we're going to call the set meta table function.
We're going to refer to our object.
And we're going to attach the rectangle class to it.
And now because this object has this rectangle class, what's going to happen is if we use the dot operator
as you can see we're going to see a whole bunch of other things in here such as our get area function.
And that's because if we go through the logic of the interpreter, when we try to index it with, for
example, a key like get area, it's first going to check to see if this key exists within our object.
Obviously our object doesn't have a key called get area.
So the next thing it's going to do is it's going to check if our table has a meta table, which it does
because we set it right here.
Then it's going to check to see if that meta table has an underscore underscore index meta method which
it does.
We defined it right here.
And then it's going to access the value inside of the meta method which is going to be either a function
or a table.
Since it's a table what it's going to do is it's going to check this table right here to see if it has
that key we're looking for.
And guess what?
We declared a function inside of our rectangle class that has that exact same name.
So it's going to use that as a function.
And that's why we are able to see it with the intellisense and autofill it in our code.
So let's go ahead and finish filling out our get area function.
And all this needs to do is just return to us the value of the width multiplied by the height.
That's how we get the area of our rectangle.
Now an issue you're going to notice is wait, how do we know what the width and the height is of our
object within this function?
How do we how do we refer to our object?
Maybe I guess we're just going to have to create a parameter right where we pass our object to it.
And then we could do req to get area rect.
But of course.
This right here.
Looks looks a little weird.
Why do we have to pass wrecked to our function when we're calling the function from our rectangle object?
Well, what we need to do is we need to replace declaring this function with a dot and use a colon.
And now what has happened is that we're telling the interpreter that this function right here is a method
or aka this function is going to belong to a table.
And because we're doing this now, we have access to a hidden parameter in our function called self.
As you can see, it pops up right here.
This hidden parameter is important because like I mentioned, we need to be able to refer to the object
that our function is being called upon.
So you can think of self as a special variable that always points us to the object on which our method
is being called.
So this way, instead of having to pass our rectangle directly to our method, we can just simply use
a colon and call our get area function.
And now our object is going to be automatically passed to our get area method.
And it's going to be stored inside of our hidden variable called self.
So that means if I were to index self, we're going to be able to have access to the width and the height
properties of our object.
So calling our method this way is the exact same way of doing it like this where we use a dot instead
and we pass the rectangle to the function doing it.
These two ways are the exact same thing.
It's just doing it this way.
It's a lot easier and faster to type out.
So now what we can do in our get area function is we can simply return the width of our object multiplied
by the height of our object.
And now let's go ahead and print that value out into the console.
And then let's go ahead and change up these values.
Let's make the width ten and the height ten.
And then if we run this code as you can see we're going to get the value printed of 100 which is ten
multiplied by ten.
And that is the area of our rectangle.
Look at that.
It works.
Now if we declared this method by using a dot and we called the function using a dot, what you're going
to see is first off, the linter in the script editor is going to tell us that it doesn't know what
self is.
And that's because we didn't declare this function as a method.
But if we go ahead and try to run this code, what you're going to see is we're going to run into an
error.
And the error says attempt to index nil with the width on line 14.
So right here it says we tried to index nil with width.
And that makes sense because self is going to be nil here.
It doesn't know what self is.
And we're not able to pass our object directly to our method.
That's why we have to use a colon.
And that's why when we call our function we need to use a colon as well.
Okay.
Let's create another method for our class and let's have this new method give us the parameter of our
rectangle.
So we can refer to our rectangle class.
Create a new method.
And this one we could call it get parameter.
And we can basically do the exact same thing that we did up here.
We're going to return self but we're going to do plus the height.
And then we need to do this first.
And then we can go ahead and multiply this by two.
And this will give us the perimeter of our rectangle.
So let's create another new rectangle using rectangle dot new.
Let's make the width five and the height ten.
And then let's go ahead and print the perimeter of our rectangle.
So if we run this code as you can see we're going to get a value of 30 which is the perimeter for our
rectangle.
Very cool.
So our class is coming together nicely.
Now let's say we wanted to be able to add two rectangles together.
For example let's say I had rectangle one here and then I had another rectangle here.
We could call this rectangle dot new.
And we'll do like ten and ten.
And let's say I wanted to create a brand new rectangle.
Let's call it rectangle three.
And that's going to be equal to our first rectangle plus our second rectangle.
Well, in order to do this operation right here we're going to have to add an underscore underscore
add meta method into our rectangle class.
So as a challenge for you, I would like for you to create a working underscore underscore add meta
method to our class.
And then you can go ahead and pause the lecture right now.
Take a few minutes to do that.
And then I will show you how I would approach it next.
Okay.
Let's go ahead and create an underscore underscore add meta method to our rectangle class.
So we could do rectangle dot underscore underscore add.
And we want to set that equal to a new function.
And this function of course is going to be passed our first rectangle.
We'll call it rect one.
And it's going to be passed our second rectangle.
We'll call that rect two.
And then here we need to return a brand new rectangle object that let's say has the width of rect one
and rect two added together, as well as both heights added together.
And the easiest way to do that would be to just use our already pre-existing constructor, our new constructor,
and all we need to do is just pass a new width and a new height.
So we could do rectangle one dot width and then we can go ahead and add rectangles two width.
And then for the height we can refer to rectangle one's height and add a rectangle two's height.
And then we can just return this new object from our add meta method.
And just like that we're done.
So we can go ahead and print out rectangle three.
Actually let's go ahead and print out all of these rectangles.
So we'll print rectangle one.
We'll print a rectangle two.
And then we'll print rectangle three.
And then let's copy this code and run it in the command line.
There we go.
We got three objects printed out.
So let's go ahead and take a look at each of them.
And as you can see here is our first object our first rectangle with the height of ten a width of five.
Here's our second with a height and width of ten.
And then here is our brand new rectangle that had both the height and the width of the first two rectangles
added together.
Now what if we would like to be able to multiply two rectangles together instead?
Well, now we're going to need a meta method called underscore underscore mul short for multiply.
So I would also like for you to attempt to create a working multiplication and meta method for our class,
using the same logic that we used for our add meta method.
So pause the lecture.
Now take a few minutes and I'll show you my solution next okay.
So let's go ahead and define inside of our rectangle class a new underscore underscore multiplication
meta method.
And we're going to set that equal to a function.
And this function is also going to be passed our two rectangles.
And then what we need to do is basically the exact same thing that we did for our addition meta method.
But instead of adding the widths and the heights together, we can just simply multiply them together.
And there we go.
Just like that, we now have a meta method for multiplication.
So if we were to run this brand new code, let's go ahead and see what outputs we get.
There is our first two rectangle objects.
And then our third one is look at that.
We have our height of 110 multiplied by ten.
And then we have our width of 50 which is ten multiply by five.
So our multiplication meta method is working great.
Now we could definitely keep adding more and more functionality to our class, and we could create more
and more meta method definitions.
But I think this is a pretty good spot to stop and a recap over what we just learned.
So object oriented programming is a way for us to create objects from classes.
Since Lua does not have a built in way to create classes, we have to imitate object oriented programming
using tables and meta tables.
Right?
With our rectangle class.
Here we have a function that belongs to the class.
We called it dot new.
And this is our constructor.
And our constructor takes a width and a height for creating new objects.
So we create a new object here which is just an.
Empty table and we create two new properties inside of this object, which is the width and the height.
And we set that equal to the width and the height passed to our constructor.
Then afterwards we return our object with our rectangle class attached to it as the meta table.
So that way our object has access to the methods such as get area and get perimeter.
And it also has the functionality for being able to add rectangles together as well as multiply rectangles
together.
And the only reason why it's able to access these methods is because we have defined an underscore underscore
index meta method that refers to the class itself.
And then it will check inside of our class for these functions, which are right here.
And then to finally demonstrate why this table right here is our class.
Let's go ahead and just print out our rectangle table into the console.
And if we take a look at it, as you can see it has our functions or methods of get area get perimeter.
It has our meta methods of add index and multiply.
And as you can see it also has our dot new constructor.
So this table right here is acting as our class.
All right.
And guess what.
That is all from me for this lecture.
If this was way too much information for you to absorb right away, don't feel ashamed.
Try to attempt the next quiz on classes and constructors.
But if you fail the quiz, just continue moving on through the course and you can come back to this
lecture at a later time.
If you're feeling pretty confident with object oriented programming, then I would encourage you to
continue adding more features to our rectangle class, or even create your own class from scratch.
As I stated in the beginning, this lecture is mainly for just exposing you to this new concept, and
we will touch more on object oriented programming later in the course.
Otherwise, thanks for sticking through this heap of new knowledge and I will see you in the next lecture.